const mongo = require("../app/lib/mongo");
class UserAuth {
    /**
     * 这里需要对传入的base及解析完后的字符串做格式验证
     * @param base
     */
    constructor(base, url){
        let string = new Buffer(base.replace("Basic ", ""), 'base64').toString();
        let kv = string.split(":");
        this.tel = kv[0];
        this.appKey = kv[1];
        this.check = false;
        this.currentPack = null;
        this.packCounter = null;
        this.url = url;
        this.moneyCache = null;
        this.db = {};
        this.db.user = new mongo(mydb, 'app.user');
        this.db.userUseLog = new mongo(mydb, 'app.user.use.log');
        this.db.userMoneyCache = new mongo(mydb, 'app.user.money.cache');
        this.db.record = new mongo(mydb, 'app.proxy.ip.record');
        this.db.pack = new mongo(mydb, 'app.user.package');
        this.db.packUse = new mongo(mydb, 'app.user.package.use');
        this.db.buyLog = new mongo(mydb, 'app.user.buy.log');
        this.db.useCache = new mongo(mydb, 'app.user.cache');
        this.db.useRule = new mongo(mydb, 'app.user.rule');
        let now = new Date();
        now.setHours(0, 0, 0, 0);
        this.day = now;
    }

    /**
     * 检查token有效性，先查是否存在此账户，再查有效性（如余额、次数是否合规）
     * 定价策略：
     * 套餐：包日、包周、包月、包季、包年。再按每日使用总量分档1w次起，2w次，4w次，8w次，无限
     * 按次计费 0.0004元
     * @returns {Promise<boolean>}
     */
    async checkIt (){
        try{
            let userInfo = await this.db.user.findOne({tel:this.tel, appKey:this.appKey});
            if(!userInfo) return false;
            this.userInfo = userInfo;
            this.check = true;
            return true;
        }catch (e) {
            throw e
        }
    }

    /**
     *
     * @returns {Promise<void>}
     */
    async checkPack (){
        try{
            if(!this.userInfo) await this.checkIt();
            //查找当前正在使用的套餐
            let pack = await this.db.packUse.findOneAndUpdate({uid: this.userInfo._id, day:this.day, done:false}, {$inc:{count:1}}, {upsert: false, returnOriginal:false});
            if(!pack.value){
                //查找可以使用的套餐
                let packList = await this.db.pack.findOneAndUpdate({uid: this.userInfo._id, lastUse:{$ne:this.day}, end_at: { $gt: new Date() }}, {$set:{lastUse:this.day}}, {sort:{created_at:1}, upsert: false, returnOriginal:false});
                if(!packList.value){
                    //检查余额是够足够
                    if(this.userInfo.money && this.userInfo.money.balance > 0.0004){
                        //余额足够则先预扣除0.0004元，待请求成功后再做
                        let userInfo = await this.db.user.findOneAndUpdate({_id:this.userInfo._id}, {$inc:{"money.balance": - 0.0004, "money.cost":0.0004}}, {returnOriginal:false});
                        this.userInfo = userInfo.value;
                        let cacheInfo = await this.db.userMoneyCache.insert({
                            url: this.url
                            , time: new Date()
                            , cost: 0.0004
                            , uid: this.userInfo._id
                        });
                        this.moneyCache = cacheInfo.ops[0];
                    } else {
                        //余额不足
                        return false;
                    }
                } else {
                    this.currentPack = packList.value;
                    //插入正在使用套餐记录
                    let insertPack = await this.db.packUse.insert({
                        uid: this.userInfo._id
                        , day:this.day
                        , done: false
                        , count:1
                        , pack_id: packList.value._id
                    });
                    this.packCounter = insertPack.ops[0];
                }
            } else {
                this.currentPack = await this.db.pack.findOne({_id:pack.value.pack_id});
                this.packCounter = pack.value;
            }
            return true;
        }catch (e) {
            throw e
        }
    }

    /**
     * 请求成功后操作--记录用户的使用日志
     * @param url
     * @param method
     * @param statusCode
     * @param header
     * @param body
     * @returns {Promise<void>}
     */
    async newRecord(url, method, statusCode, header, body, proxyInfo, requestInfo){
        try{
            //使用套餐次数
            if(this.packCounter){
                await this.db.record.insert({ url, method, statusCode, header, uid: this.userInfo._id, pack_id: this.currentPack._id, i: this.packCounter.count, time: new Date(), proxyInfo, requestInfo});
                if(this.packCounter.count >= global.package[this.currentPack.index].level[this.currentPack.level_id].limit){
                    await this.db.packUse.update({_id: this.packCounter._id}, {$set:{done:true}});
                }
            }
            //按次计费
            if(this.moneyCache){
                await this.db.record.insert({ url, method, statusCode, header, uid: this.userInfo._id, pack_id: null, i: 1, time: new Date(), proxyInfo, requestInfo});
                await this.db.userMoneyCache.remove({_id:this.moneyCache._id});
                await this.db.buyLog.update({
                    uid:this.userInfo._id,
                    day:this.day,
                    desc:"按次收费"
                }, {$inc:{count:1, "level.cost":0.0004}, $set:{created_at:new Date()}})
            }
            await this.db.userUseLog.update({uid:this.userInfo._id, day:this.day}, {$inc:{count:1}});
        }catch (e) {
            throw e
        }
    }

    /**
     * 请求失败后操作--恢复次数、或返回代扣金额
     * @returns {Promise<void>}
     */
    async fail(){
        try{
            if(this.packCounter){
                let packUseInfo = await this.db.packUse.findOneAndUpdate({_id: this.packCounter._id}, {$inc:{count:-1}}, {upsert: false, returnOriginal:false});
                if(packUseInfo.count < global.package[this.currentPack.index].level[this.currentPack.level_id].limit){
                    await this.db.packUse.update({_id: this.packCounter._id}, {$set:{done:false}});
                }
            }
            if(this.moneyCache){
                await this.db.user.update({_id:this.userInfo._id}, {$inc:{"money.balance": 0.0004, "money.cost":-0.0004}});
                await this.db.userMoneyCache.remove({_id:this.moneyCache._id});
            }
        }catch (e) {
            throw e
        }
    }

    /**
     * 缓存数据
     * @returns {Promise<void>}
     */
    async cache(url, method, response, length) {
        try {
            await this.db.useCache.insert({ uid: this.userInfo._id, url, method, response, length });
        } catch (e) {
            throw e
        }
    }

    /**
     * 是否已经缓存
     * @param url
     * @param method
     * @returns {Promise<void>}
     */
    async isCache(url, method) {
        try {
            return await this.db.useCache.findOne({ uid: this.userInfo._id, url, method });
        } catch (e) {
            throw e
        }
    }
    /**
     * 是否被过滤
     * @param url
     * @param method
     * @returns {Promise<void>}
     */
    async isFilter(url) {
        try {
            let ruleList = await this.db.useRule.find({ uid: this.userInfo._id }), isResource = false;
            for(let rule of ruleList){
                let reg = new RegExp(rule.rule);
                let res = reg.test(url);
                if(res){
                    isResource = true;
                    break;
                }
            }
            return isResource;
        } catch (e) {
            throw e
        }
    }
}

module.exports =  UserAuth;